#include "KeyCapture.h"

KeyCapture::KeyCapture()
{
    m_focusedAppPID = 0;
    m_focusedApp = m_focusedAppElement = NULL;
    m_focusedAppObserver = NULL;
    m_runLoop = NULL;
}

KeyCapture::~KeyCapture()
{

}

void KeyCapture::ObserveKeyDown(AXUIElementRef element)
{
    AXError err;
    CFNumberRef cfNumber = NULL;
    err = AXUIElementCopyAttributeValue(element, kAXNumberOfCharactersAttribute, (CFTypeRef *)&cfNumber);
    if (err != kAXErrorSuccess)
    {
        printf("AXUIElementCopyAttributeValue with kAXNumberOfCharactersAttribute failed [%d]\n", err);
        return;
    }
    printf("current text length:\n");
    CFShow(cfNumber);
    printf("end of current text length:\n");
    CFRelease(cfNumber);

    /*********************************/

    err = AXUIElementCopyAttributeValue(element, kAXInsertionPointLineNumberAttribute, (CFTypeRef *)&cfNumber);
    if (err != kAXErrorSuccess)
    {
		printf("AXUIElementCopyAttributeValue with kAXInsertionPointLineNumberAttribute failed [%d]\n", err);
//		return;
	}
    printf("current insertion line:\n");
    CFShow(cfNumber);
    printf("end of current insertion line:\n");

    /*********************************/

    CFRange cfRange = CFRangeMake(0, 0);
    AXValueRef axValue = NULL;
    err = AXUIElementCopyAttributeValue(element, kAXSelectedTextRangeAttribute, (CFTypeRef *)&axValue);
    if (err != kAXErrorSuccess)
    {
		printf("AXUIElementCopyAttributeValue with kAXSelectedTextRangeAttribute failed [%d]\n", err);
//		return;
	}
    AXValueGetValue(axValue, kAXValueCFRangeType, (void*) &cfRange);
    printf("current selected text range location: %d\n", cfRange.location);
    printf("current selected text range length: %d\n", cfRange.length);
    if (axValue)
        CFRelease(axValue);

}

void KeyCapture::FocusedWindowChangedHandler(AXObserverRef observer, AXUIElementRef element,
        CFStringRef notification, void *refcon)
{
    AXError err;
	KeyCapture* pKeyCapture = (KeyCapture*) refcon;

	if (kCFCompareEqualTo == CFStringCompare(notification, kAXValueChangedNotification, 0))
    {
//        printf("address stored element: %p\n", pKeyCapture->m_focusedAppElement);
//        printf("address new element: %p\n", element);
//
//        pid_t pid;
//        AXUIElementGetPid(pKeyCapture->m_focusedAppElement, &pid);
//        printf("pid of stored element: %d\n", pid);
//        AXUIElementGetPid(element, &pid);
//        printf("pid of new element: %d\n", pid);
//        if (pKeyCapture->m_focusedAppElement == element)
//        {
        printf("Text edited\n");
        pKeyCapture->ObserveKeyDown(element);
        return;
//        }
	}

	if (kCFCompareEqualTo == CFStringCompare(notification, kAXFocusedUIElementChangedNotification, 0))
    {
        printf("element changed\n");
//        printf("address stored element: %p\n", pKeyCapture->m_focusedAppElement);
//        printf("address new element: %p\n", element);
//
//        pid_t pid;
//        AXUIElementGetPid(pKeyCapture->m_focusedAppElement, &pid);
//        printf("pid of stored element: %d\n", pid);
//        AXUIElementGetPid(element, &pid);
//        printf("pid of new element: %d\n", pid);

//        CFRunLoopRemoveSource(CFRunLoopGetMain(), AXObserverGetRunLoopSource(pKeyCapture->m_focusedAppObserver), kCFRunLoopDefaultMode);
        err = AXObserverRemoveNotification(pKeyCapture->m_focusedAppObserver,
                pKeyCapture->m_focusedAppElement, kAXValueChangedNotification);
        if (err)
        {
            printf("AXObserverRemoveNotification (kAXValueChangedNotification) failed [%d]\n", err);
//            return;
        }
        CFRelease(pKeyCapture->m_focusedAppElement);
//        pKeyCapture->m_focusedAppElement = NULL;

        pKeyCapture->m_focusedAppElement = (AXUIElementRef) CFRetain(element);
//        pKeyCapture->m_focusedAppElement = element;
        err = AXObserverAddNotification(pKeyCapture->m_focusedAppObserver,
                pKeyCapture->m_focusedAppElement, kAXValueChangedNotification, pKeyCapture);
        if (err)
        {
            printf("AXObserverAddNotification (kAXValueChangedNotification) failed [%d]\n", err);
//            return;
        }
//        CFRunLoopAddSource(CFRunLoopGetMain(), AXObserverGetRunLoopSource(pKeyCapture->m_focusedAppObserver), kCFRunLoopDefaultMode);
        return;
	}
}

bool KeyCapture::InstallAXUIEventMonitor(pid_t pid)
{
    AXError err;
    m_focusedApp = AXUIElementCreateApplication(pid);

    err = AXObserverCreate(m_focusedAppPID, FocusedWindowChangedHandler, &m_focusedAppObserver);
    if (err)
    {
        printf("AXObserverCreate failed [%d]\n", err);
		m_focusedAppObserver = NULL;
		return false;
	}

    err = AXObserverAddNotification(m_focusedAppObserver, m_focusedApp,
            kAXFocusedUIElementChangedNotification, this);
	if (err)
    {
        printf("AXObserverAddNotification with kAXFocusedUIElementChangedNotification failed [%d]\n", err);
		return false;
	}

    err = AXUIElementCopyAttributeValue(m_focusedApp, kAXFocusedUIElementAttribute, (CFTypeRef*) &m_focusedAppElement);
    if (err)
    {
        printf("AXUIElementCopyAttributeValue with kAXFocusedUIElementAttribute failed [%d]\n", err);
		return false;
    }

    err = AXObserverAddNotification(m_focusedAppObserver, m_focusedAppElement,
            kAXValueChangedNotification, this);
	if (err)
    {
		printf("AXObserverAddNotification (kAXValueChangedNotification) failed [%d]\n", err);
        return false;
	}

//	(void)ObserveKeyDown(ACTION_FOCUSED_WINDOW_CHANGED, pid, 0, NULL);

	CFRunLoopAddSource(CFRunLoopGetMain(), AXObserverGetRunLoopSource(m_focusedAppObserver), kCFRunLoopDefaultMode);
//	CFRelease(m_focusedAppElement);

    return true;
}

void KeyCapture::UninstallAXUIEventMonitor()
{
    AXError err;

//    CFRunLoopRemoveSource(CFRunLoopGetMain(), AXObserverGetRunLoopSource(m_focusedAppObserver), kCFRunLoopDefaultMode);
    
    err = AXObserverRemoveNotification(m_focusedAppObserver, m_focusedAppElement, kAXValueChangedNotification);
    if (err)
    {
        printf("AXObserverRemoveNotification (kAXValueChangedNotification) failed [%d]\n", err);
        return;
    }
//    CFRelease(m_focusedAppElement);
    m_focusedAppElement = NULL;

    err = AXObserverRemoveNotification(m_focusedAppObserver, m_focusedApp, kAXFocusedUIElementChangedNotification);
    if (err)
    {
        printf("AXObserverRemoveNotification (kAXFocusedUIElementChangedNotification) failed [%d]\n", err);
        return;
    }
    CFRelease(m_focusedApp);
    m_focusedApp = NULL;

    CFRelease(m_focusedAppObserver);
    m_focusedAppObserver = NULL;
}

OSStatus KeyCapture::AppFrontSwitchedHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
    printf("AppFrontSwitchedHandler called------------\n");
    KeyCapture *pKeyCapture = (KeyCapture*) inUserData;
	OSStatus err;

	err = GetEventParameter(inEvent, kEventParamProcessID, typeProcessSerialNumber,
            NULL, sizeof(pKeyCapture->m_focusedAppPSN), NULL, &pKeyCapture->m_focusedAppPSN);
	if (noErr != err)
    {
		printf("GetEventParameter (kEventParamProcessID) failed\n");
		GetFrontProcess(&pKeyCapture->m_focusedAppPSN);
	}

	CFStringRef processName;
	if(noErr == CopyProcessName(&pKeyCapture->m_focusedAppPSN, &processName))
	{
        printf("Process name: \n");
		CFShow(processName);
		CFRelease(processName);
        printf("End of Process name\n");
	}

    GetProcessPID(&pKeyCapture->m_focusedAppPSN, &pKeyCapture->m_focusedAppPID);

    if (pKeyCapture->m_focusedApp)
    {
        pKeyCapture->UninstallAXUIEventMonitor();
    }

    if (!pKeyCapture->InstallAXUIEventMonitor(pKeyCapture->m_focusedAppPID))
    {
        printf("Could not install AXUIEventMonitor\n");
    }

    AXUIElementRef focusedAppWindow;
    err = AXUIElementCopyAttributeValue(pKeyCapture->m_focusedApp, kAXFocusedWindowAttribute, (CFTypeRef *)&focusedAppWindow);
    if (err != kAXErrorSuccess)
    {
		printf("AXUIElementCopyAttributeValue with kAXFocusedWindowAttribute failed [%d]\n", err);
		CFRelease(pKeyCapture->m_focusedApp);
        pKeyCapture->m_focusedApp = NULL;
		return err;
	}

    CFStringRef focusedAppWindowTitle;
    err = AXUIElementCopyAttributeValue(focusedAppWindow, kAXTitleAttribute, (CFTypeRef *)&focusedAppWindowTitle);
    if (err != kAXErrorSuccess)
    {
		printf("AXUIElementCopyAttributeValue with kAXTitleAttribute failed [%d]\n", err);
		CFRelease(focusedAppWindow);
		CFRelease(pKeyCapture->m_focusedApp);
        pKeyCapture->m_focusedApp = NULL;
		return err;
	}
    printf("window title: \n");
    CFShow(focusedAppWindowTitle);
    CFRelease(focusedAppWindowTitle);
    printf("end of window title\n");

    AXUIElementRef focusedAppUIElement;
    err = AXUIElementCopyAttributeValue(pKeyCapture->m_focusedApp, kAXFocusedUIElementAttribute, (CFTypeRef *)&focusedAppUIElement);
    if (err != kAXErrorSuccess)
    {
		printf("AXUIElementCopyAttributeValue with kAXFocusedUIElementAttribute failed [%d]\n", err);
		CFRelease(pKeyCapture->m_focusedApp);
        pKeyCapture->m_focusedApp = NULL;
		return err;
	}

    

//    cfRange.length = 0;
//    rangeAttr = AXValueCreate(kAXValueCFRangeType, (const void *)&cfRange);
//	CFStringRef rangeString = NULL;
//	err = AXUIElementCopyParameterizedAttributeValue(focusedAppUIElement, kAXStringForRangeParameterizedAttribute, rangeAttr, (CFTypeRef *) &rangeString) ;
//	if (kAXErrorSuccess != err)
//	{
//		printf("AXUIElementCopyParameterizedAttributeValue with kAXStringForRangeParameterizedAttribute failed [%d]\n", err);
//	}
//    printf("string for range:\n");
//    CFShow(rangeString);
//    printf("end of string for range:\n");
//    CFRelease(rangeString);
//
//
//    err = AXUIElementCopyParameterizedAttributeValue(focusedAppUIElement, kAXRangeForLineParameterizedAttribute, cfNumber, (CFTypeRef *) &rangeAttr);
//	if (kAXErrorSuccess != err)
//	{
//		printf("AXUIElementCopyParameterizedAttributeValue with kAXRangeForLineParameterizedAttribute failed [%d]\n", err);
//	}
//    AXValueGetValue(rangeAttr, kAXValueCFRangeType, (void*) &cfRange);
//    printf("range for line location: %d\n", cfRange.location);
//    printf("range for line length: %d\n", cfRange.length);


	return noErr;
}
